# coding: utf-8

import datetime
import time
import json

from flask import request, current_app
from flask_restful import Api

from common.red_packet import random_money, average_money
from common.schedule.red_packet_tasks import red_packet_expired_task
from app.extensions import db
from app.op.constant import RedPacketType, BidCostType
from common.constant import RedPacketStatus, RECEIVE_RED_PACKET_HISTORY
from app.op.models import (
    BidPointRedPacket, GetBidPointRedPacket, BidCostRecord
)
from app.api.codes import PARAMS_ILLEGAL
from app.api_v2.constant import RED_PACKET_EXPIRED_SECOND
from app.api_v2.views import Response, AppAuthResource
from app.api_v2.red_packet_schemas import (
    send_red_packet_schema, receive_red_packet_schema,
    receive_red_packet_history_schema, send_red_packet_history_schema
)
from app.api_v2.red_packet_codes import (
    NOT_ENOUGHT_BID_POINT, PACKET_COUNT_GE_TOTAL_MONEY,
    RED_PACKET_NOT_EXIST, RED_PACKET_EXPIRED, RED_PACKET_OVERED
)

api = Api()


@api.resource('/red_packet')
class RedPacket(AppAuthResource):
    """
    add at 2018-07-25
    汇币红包
    """

    def put(self):
        """
        领红包
        """
        # STEP1. 参数校验
        data, errors = receive_red_packet_schema.load(request.get_json())
        if errors:
            return Response(
                PARAMS_ILLEGAL.new_format_msg(list(errors.values())[0][0]))
        red_packet_id = data['red_packet_id']

        # STEP2. 判断红包是否存在/已经过期/已经领取完
        red_packet = BidPointRedPacket.get_by_id(red_packet_id)
        if not red_packet:
            return Response(RED_PACKET_NOT_EXIST)
        now = int(time.time())
        if now - red_packet.created_at >= RED_PACKET_EXPIRED_SECOND:
            return Response(RED_PACKET_EXPIRED)
        if red_packet.status == RedPacketStatus.OVER.value:
            return Response(RED_PACKET_OVERED)
        # STEP3. 红包没有过期，触发领取红包消费者
        current_app.assign_red_packet_producer.send(
            json.dumps(dict(
                func='assign_red_packet',
                args=(),
                kwargs=dict(
                    red_packet_id=red_packet_id,
                    user_id=self.user.id,
                )
            ))
        )

        return Response()

    def post(self):
        """
        发红包，即创建红包
        """
        data, errors = send_red_packet_schema.load(request.get_json())
        if errors:
            return Response(
                PARAMS_ILLEGAL.new_format_msg(list(errors.values())[0][0])
            )

        # STEP1. 校验输入
        # STEP1.1 判断发红包的人汇币是否足够
        if self.user.pack.bid_point < data['total_money']:
            return Response(
                NOT_ENOUGHT_BID_POINT,
                body={'bid_point': self.user.pack.bid_point}
            )
        # STEP1.2 红包的汇币是否大于等于红包数量
        if data['packet_count'] > data['total_money']:
            return Response(PACKET_COUNT_GE_TOTAL_MONEY)

        # STEP2. 写 BidPointRedPacket 记录，扣汇币
        total_money, packet_count = data['total_money'], data['packet_count']
        red_packet = BidPointRedPacket.create(
            sender_id=self.user.id,
            status=RedPacketStatus.NORMAL.value,
            **data
        )
        self.user.pack.bid_point -= total_money
        red_packet_info = dict(
            overed_at=red_packet.overed_at,
            total_money=red_packet.total_money,
            remark=red_packet.remark,
            packet_type=red_packet.packet_type,
            packet_count=red_packet.packet_count,
            status=red_packet.status,
            sender_id=red_packet.sender_id,
            sender_nickname=red_packet.sender_nickname,
            sender_avatar=red_packet.sender_avatar,
            created_at=red_packet.created_at
        )

        # STEP3. 如果是群红包，预先分配每个红包的汇币，并写入GetBidPointRedPacket，receiver_id=0
        # 群红包
        if not data['receiver_id']:
            if red_packet.packet_type == RedPacketType.RANDOM.value:
                each_money = random_money(total_money, packet_count)
            else:
                each_money = average_money(total_money, packet_count)

            # 写 GetBidPointRedPacket
            for i in each_money:
                tmp_r = GetBidPointRedPacket(
                    red_packet_id=red_packet.id,
                    packet_type=red_packet.packet_type,
                    sender_id=red_packet.sender_id,
                    money=i,
                )
                db.session.add(tmp_r)

        # TODO 个人红包逻辑，暂时不实现
        else:
            pass

        # STEP4. 写 BidCostRecord ，记录汇币消耗记录
        BidCostRecord.create(
            user_id=self.user.id,
            cost_type=BidCostType.SEND_RED_PACKET.value,
            bid_point=total_money,
            left_bid_point=self.user.pack.bid_point,
        )

        # STEP5. 24小时后启动回收红包的定时任务
        # NOTE 这里稍微在24小时的基础上再增加1分钟，防止刚好有人在24小时后抢红包
        # 同时在抢红包的接口里，直接判断该红包是否已经被回收
        now = datetime.datetime.utcnow()
        red_packet_expired_task.apply_async(
            (red_packet.id,),
            eta=now + datetime.timedelta(
                seconds=RED_PACKET_EXPIRED_SECOND + 60),
            expires=now + datetime.timedelta(
                seconds=RED_PACKET_EXPIRED_SECOND + 60 * 3)
        )
        current_app.redis.set(
            RECEIVE_RED_PACKET_HISTORY.format(red_packet_id=red_packet.id),
            json.dumps(
                dict(
                    red_packet_info=red_packet_info,
                    result=list(),
                    polling=True
                )
            )
        )
        return Response(body={'red_packet_id': red_packet.id})


@api.resource('/receive_red_packet/<int:red_packet_id>')
class ReceiveRedPacket(AppAuthResource):
    """
    created by lina at 2018.07.26
    轮询抢红包结果
    """

    def get(self, red_packet_id):

        records = current_app.redis.get(
            RECEIVE_RED_PACKET_HISTORY.format(red_packet_id=red_packet_id)
        )
        if records is not None:
            records = json.loads(records)
        else:
            records = dict(
                polling=True,
                result=list(),
                red_packet_info=dict()
            )

        # STEP1 GetBidPointRedPacket 中有该用户抢红包的记录，更新用户抢到的红包数
        received_money = 0
        for each in records.get('result', list()):
            if self.user.id == each.get('receiver_id'):
                received_money = each.get('money', 0)
                records['polling'] = False
                break
        records['received_money'] = received_money
        red_packet_info = records.get('red_packet_info', dict())
        status = red_packet_info.get('status')
        if status in (RedPacketStatus.EXPIRED.value, RedPacketStatus.OVER.value):
            records['polling'] = False

        return Response(body=records)


@api.resource('/red_packet_history')
class RedPacketHistory(AppAuthResource):
    """
    created by lina at 2018.07.28
    发/收红包历史记录
    """

    def post(self):
        data = request.get_json()
        result = []
        total_count = 0
        total_account = 0
        best_receiver_count = 0
        nickname = self.user.nickname
        avatar = self.user.avatar_url
        if data['type'] == 'receiver':
            received_red_packet = GetBidPointRedPacket.query.filter(
                GetBidPointRedPacket.receiver_id == self.user.id,
            )
            total_count = received_red_packet.count()
            total_account = sum([each.money for each in received_red_packet])
            best_receiver_count = received_red_packet.filter(
                GetBidPointRedPacket.best_receiver.is_(True)
            ).count()
            result = receive_red_packet_history_schema.dump(received_red_packet)[0]

        if data['type'] == 'sender':
            sent_red_packet = GetBidPointRedPacket.query.filter(
                GetBidPointRedPacket.sender_id == self.user.id,
                GetBidPointRedPacket.receiver_id != 0
            ).all()
            total_account = sum([each.money for each in sent_red_packet])
            total_count = len(sent_red_packet)

            red_packet = BidPointRedPacket.query.filter(
                BidPointRedPacket.sender_id == self.user.id,
            ).all()
            for i in red_packet:
                i.received_count = i.get_records.filter(
                    GetBidPointRedPacket.receiver_id != 0
                ).count()
            result = send_red_packet_history_schema.dump(red_packet)[0]
        return Response(body={
            'result': result,
            'total_count': total_count,
            'total_account': total_account,
            'best_receiver_count': best_receiver_count,
            'nickname': nickname,
            'avatar': avatar
        })
